// Main.cpp
//
// Copyright (c) 2008 Bojan Rajkovic
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
//

#include "../include/AllPurpose.h"
#include "../include/Runner.h"
#include <signal.h>

LinPulse::Runner* runner;

void terminate(int parameter) {
	runner->Terminate();
}

void interrupt(int parameter) {
	runner->Terminate();
}

void hangup(int parameter) {
	runner->SendPulse();
}

void Daemonize() {
	Utility::LogMessage(_("Daemonizing keycounter."), M_INFO);
		
		// Fork the parent process
		pid_t pid;
		pid = fork();

		// Write the child's PID to the log and to the pid file so we can kill the daemon.
		remove("/var/run/keycounter.pid");
		ofstream pidFile ("/var/run/keycounter.pid");
		if (!pidFile.good()) {
			Utility::LogMessage(_("Could not open PID file for writing, maybe the daemon isn't running as root?"), M_ERROR);
			perror("ofstream");	
			exit(EXIT_FAILURE);
		}
		if (pid != 0) { 
			pidFile << pid << endl;
			// log the child pid and write it out to the pid file
			// so we can kill the daemon from outside
			Utility::LogMessage(Utility::FormatString(_("Child PID: %i"), pid), M_INFO);
			pidFile.close();
		};


		// If we got a bad PID, fail out, if we got a good PID, we're all set.
		if (pid < 0) {
			exit(EXIT_FAILURE);
		}
		if (pid > 0) {
			// this kills the parent process
			exit(EXIT_SUCCESS);
		}

		// Past this point, we're working entirely with the child process
		// The parent process has been killed by the above exit().

		// Set the umask so we can access files written by the daemon
		umask(0);

		// Get a new SID
		pid_t sid;
		sid = setsid();

		// error out if the sid is < 0
		if (sid < 0) {
			Utility::LogMessage(_("Could not get valid SID for keycounter daemon, exiting."), M_ERROR);
			exit(EXIT_FAILURE);
		}

		// Change to the daemon's "home" directory 
		if ((chdir("/var/lib/pulse/")) < 0) {
			Utility::LogMessage(_("Could not change to the daemon home directory, exiting."), M_ERROR);
			exit(EXIT_FAILURE);
		}

		// Close all the standard file descriptors
		close(STDIN_FILENO);
		close(STDOUT_FILENO);
		close(STDERR_FILENO);

		// log a message informing that we've finished daemonizing
		Utility::LogMessage(_("Finished daemonizing."), M_INFO);	
}
	
int main(int argc, char* argv[]) {
	
	// some handy variables for getopt_long
	int opt, longopt_index = 0;
	
	// set the locale for gnu gettext to know where to look
	// for localized messages
	setlocale (LC_MESSAGES, "");
  	textdomain ("keycounter");
  	bindtextdomain ("keycounter", LOCALEDIR);
	
	// Let's also set catches for SIGTERM and SIGINT--we just wanna log that we're terminating
	// there's nothing to actually clean up when terminated
	void (*prev_fn)(int);
	prev_fn = signal (SIGTERM,terminate);
  	if (prev_fn==SIG_IGN) signal(SIGTERM,SIG_IGN);
	prev_fn = signal (SIGINT,interrupt);
  	if (prev_fn==SIG_IGN) signal(SIGINT,SIG_IGN);
  	prev_fn = signal (SIGHUP,hangup);
  	if (prev_fn==SIG_IGN) signal(SIGHUP,SIG_IGN);
	
	// Let's parse the config file, and pull relevant settings out of it
	// We'll let them get overwritten by command-line settings as appropriate
	Config configuration;
	configuration.readFile(strdup(DEFAULT_CONFIG_FILE));
	
	std::string eventDevice = configuration.lookup("application.keycounter.eventdevice");
	std::string outputFile = configuration.lookup("application.keycounter.outputfile");
	bool daemonize = (bool)configuration.lookup("application.system.daemonize");
	
	
	if (Utility::CheckPIDFile("/var/run/keycounter.pid")) {
		Utility::LogMessage(_("PID file exists, inside pid is valid, dying."), M_ERROR);
		cerr << _("Oops, it looks like there's already an instance running: /var/run/keycounter.pid") << endl;
		cerr << _("exists and the PID inside appears to be an existing process.") << endl;
		exit(EXIT_FAILURE);
	} else {
		Utility::LogMessage(_("PID file exists, inside PID is not valid -- keep on truckin'."), M_INFO);
	}

	// the option struct that getopt_long looks through to figure out which
	// short options to assign to a long option, and if options require paremeters
	static struct option long_options[] = {
		{"event-device", 1, NULL, 'e'},
		{"output-file", 1, NULL, 'o'},
		{"no-daemon", 0, NULL, 'n'},
		{"help", 0, NULL, 'h'},
		{"usage", 0, NULL, 'h'},
		{NULL, 0, NULL, 0} /* marks end-of-list */
	};

	// read in options via getopt_long until we don't have any more options
	// and either store the arguments or change the appropriate boolean flag
	while ((opt = getopt_long(argc, argv, "e:o:nh", long_options, &longopt_index)) > 0) {
		switch (opt) {
		case 'e':
			eventDevice = optarg;
			break;
		case 'o':
			outputFile = optarg;
			break;
		case 'n':
			daemonize = false;
			break;
		// if the option is -? or -h or --help, print the usage instructions
		case '?':
		case 'h':
		default:
			cerr << Utility::FormatString("usage: %s [options]\n", basename(argv[0]));
			cerr << _("  -e, --event-device ARG   The event device to poll when trying to read keystrokes (default: /dev/input/event0). \n");
			cerr << _("  -n, --no-daemon	Don't daemonize the keycounter (default: false). \n");
			cerr << _("  -o, --output-file ARG     The file to output keystroke statistics to (default: /var/keycounter). \n");
			cerr << _("  -v, --verbose	Output verbose, easily human-readable statistics to the output file (default: false). \n");
 			exit(EXIT_SUCCESS);
		}
	}

	// Before we start, we'll output both to the log:
	Utility::LogMessage(Utility::FormatString(_("Event device path: %s"), eventDevice.c_str()), M_INFO);
	Utility::LogMessage(Utility::FormatString(_("Output file path: %s"), outputFile.c_str()), M_INFO);

	// Do we actually want to daemonize?
	if (daemonize) {
		Daemonize();
	} else {
		// we're not daemonizing, say so and keep working.
		Utility::LogMessage(Utility::FormatString(_("Not daemonizing, PID %i."), getpid()), M_INFO);
	}
	
	runner = new Runner(eventDevice, outputFile);
	runner->Run();
}